/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.extensionpoints.palette;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.URI;
import org.eclipse.uml2.uml.Element;

import com.cea.papyrus.core.extensionpoints.AbstractExtensionPoint;
import com.cea.papyrus.core.extensionpoints.ExtensionIds;
import com.cea.papyrus.core.pseudoelement.EnhancedType;
import com.cea.papyrus.core.pseudoelement.UMLElementFactory;
import com.cea.papyrus.core.utils.Debug;

/**
 * 
 */
public class RegisteredMenu extends AbstractExtensionPoint {
	
	// The list of new PaletteMenu extensions
	/**
	 * 
	 */
	private static RegisteredMenu[] Menus;
	
	/**
	 * 
	 */
	private static List<Button> buttonFlatList;
	
	/**
	 * 
	 */
	public final IConfigurationElement	configElement;
	
	/**
	 * 
	 */
	public final int					ordinal;
	
	// Menu description
	/**
	 * 
	 */
	private final String label;
	
	/**
	 * 
	 */
	private final MenuElement[] menuElements; // Buttons and Stacks
	
	/**
	 * 
	 */
	private final Visibility visibility;

	/**
	 * 
	 * 
	 * @return 
	 */
	public String getLabel() {
		return label;
	}

	/**
	 * 
	 * 
	 * @return 
	 */
	public static RegisteredMenu[] getRegisteredMenus() {
				
		if (Menus != null) {
			return Menus;
		}
		
		List<RegisteredMenu> RegMenus = new ArrayList<RegisteredMenu>();
					
		// Reading data from plugins
		IConfigurationElement[] configElements
			= Platform.getExtensionRegistry().getConfigurationElementsFor(ExtensionIds.PALETTE_EXTENSION_ID);
		
		// Read configuration elements for the current extension
		for (int j = 0 ; j < configElements.length ; j++) {
			RegisteredMenu proxy = parseMenuExtension(configElements[j], RegMenus.size());

			if (proxy != null) {
				RegMenus.add(proxy);
			}
		} // end of configElements loop	

		
		Menus = RegMenus.toArray(new RegisteredMenu[RegMenus.size()]);
		return Menus;
	}
	
	/**
	 * 
	 * 
	 * @param ordinal 
	 * @param configElt 
	 * 
	 * @return 
	 */
	private static RegisteredMenu parseMenuExtension(IConfigurationElement configElt, int ordinal) {
		
		if (!configElt.getName().equals(IConstants.TAG_MENU)) {
			return null;
		}
		
		try {
			return new RegisteredMenu(configElt, ordinal);
			
		} catch (Exception e) {
			
			String name = configElt.getAttribute(IConstants.ATT_MENU_LABEL);
			if (name == null) {
				name = "[missing name attribute]";
			}
			String msg = "Failed to load palette extension named "
							+ name + " in "
							+ configElt
								.getDeclaringExtension()
								.getExtensionPointUniqueIdentifier();
			
			Debug.debug(null, msg, channel);
											
			return null;
		}
	}
	
	/**
	 * 
	 * 
	 * @param ordinal 
	 * @param configElt 
	 */
	public RegisteredMenu(IConfigurationElement configElt, int ordinal) {
		this.configElement = configElt;
		this.ordinal = ordinal;

		label = getAttribute(configElt, IConstants.ATT_MENU_LABEL, null, true);
		
		// Retrieve visibility
		IConfigurationElement[] children = configElt.getChildren(IConstants.TAG_VISIBILITY);
		if ((children == null) || (children.length < 1)) {
			visibility   = null;

		} else {
			Visibility proxy = Visibility.parse(children[0], 0);

			if (proxy != null) {
				visibility = proxy;
			} else {
				visibility = null;
			}		
		}

		// Retrieve menu elements
		IConfigurationElement[] elementsChildren = configElt.getChildren();
		if (elementsChildren == null) {
			menuElements = null;
			return;
		}
			
		List<MenuElement> eltList = new ArrayList<MenuElement>();
		// Read properties
		for (int i = 0 ; i < elementsChildren.length ; i++) {
			
			if ((elementsChildren[i].getName().equals(IConstants.TAG_BUTTON))
				|| (elementsChildren[i].getName().equals(IConstants.TAG_STACK))
				|| (elementsChildren[i].getName().equals(IConstants.TAG_SEPARATOR))) {
				
				MenuElement proxy = MenuElement.parse(elementsChildren[i], eltList.size());

				if (proxy != null) {
					eltList.add(proxy);
				}
			}
		} // end of configElements loop	

		if (eltList.size() < 1) {
			menuElements = null;
		} else {
			menuElements = eltList.toArray(new MenuElement[eltList.size()]);
		}
	}
	
	/**
	 * Default constructor.
	 * 
	 * @param uri 
	 * @param name 
	 */
	public RegisteredMenu(String name, URI uri) {
		this.configElement = null;
		this.ordinal = -1;
		
		label        = "undefined";
		menuElements = null;
		visibility   = null;
	}
	
	/**
	 * 
	 * 
	 * @param type 
	 * 
	 * @return 
	 */
	public boolean isVisibleInDiagram(String type) {
		
		String typeDiagramAsString = type;
		
		if (visibility == null) {
			return true;
		}
		
		if (visibility.getDiagrams() == null) {
			return true;
		}
		
		List<String> allowedDiagramTypes = new ArrayList<String>();
		for (int i = 0; i < visibility.getDiagrams().length; i++) {
			String kind = visibility.getDiagrams()[i].getKind();
			allowedDiagramTypes.add(kind);
		}
		
		if (allowedDiagramTypes.contains(typeDiagramAsString)) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * 
	 * 
	 * @return 
	 */
	public static List<Button> getAllButtonFlatList() {
		if (buttonFlatList != null) {
			return buttonFlatList;
		}
		
		RegisteredMenu[] menus = RegisteredMenu.getRegisteredMenus();
		
		// No menu detected
		if ((menus == null) || (menus.length < 1)) {
			return new ArrayList<Button>();
		}
		
		// Matching buttons
		List<Button> buttons = new ArrayList<Button>();
		for (int i = 0; i < menus.length; i++) {
			RegisteredMenu currentMenu = menus[i];
			buttons.addAll(currentMenu.getButtonFlatList());
		}
		
		buttonFlatList = buttons;
		return buttons;
	}
	
	/**
	 * 
	 * 
	 * @return 
	 */
	public List<Button> getButtonFlatList() {

		MenuElement[] menuElements = getMenuElements();
		if ((menuElements == null) || (menuElements.length < 1)) {
			return new ArrayList<Button>();
		}
		
		List<Button> list = new ArrayList<Button>();
		for (int i = 0; i < menuElements.length; i++) {
			MenuElement currentMenuElement = menuElements[i];
			
			if (currentMenuElement instanceof Button) {
				list.add((Button) currentMenuElement);
			} else if (currentMenuElement instanceof Stack) {
				list.addAll(((Stack) currentMenuElement).getButtonList());
			}
		}
		
		return list;
	}
	
	/**
	 * 
	 * 
	 * @param element 
	 * 
	 * @return 
	 */
	public static List<EnhancedType> getMatchingTypes(Element element) {
		Iterator<Button> buttonIt   = RegisteredMenu.getAllButtonFlatList().iterator();
		List<EnhancedType> matchingList = new ArrayList<EnhancedType>();

		while (buttonIt.hasNext()) {
			Button current = buttonIt.next();
			if (current.getElement().matchType(element)) {
				matchingList.add(new UMLElementFactory(current));
			}
		}
		
		return matchingList;
	}

	/**
	 * 
	 * 
	 * @return 
	 */
	public MenuElement[] getMenuElements() {
		return menuElements;
	}

	/**
	 * 
	 * 
	 * @return 
	 */
	public Visibility getVisibility() {
		return visibility;
	}
}
